

from yacf.Frontend.Shortcuts import getCurrentLanguageAst

c_ast = getCurrentLanguageAst()

from yacf.Frontend.SymbolTable import getSymbolTableFromNode, Symbol
from yacf.Tools.Declarations import decl_of_id
from yacf.Tools.Tree import getFileNode
from yacf.Frontend.FrontendFactory import partial_parse


def write_as_parameter_definition(var_list, pointer = False, reference = False):
    """ Write var_list as a parameter definition of a function
    """
    tmp = []
    for elem in var_list:
      if isinstance(elem.ptr.type, c_ast.ArrayDecl):
         name = "*" + elem.name + "_cu"
      else:
         if not pointer and not reference:
             name = elem.name
         elif reference:
             name = " & " + elem.name
         else:
             name = " * " + elem.name
      tmp += [str(elem.type) + " " + name]
    return ', '.join(tmp)


def write_as_function_call(var_list, pointer = False, value = False):
    """ Write var_list as a parameter list for a function call
    """
    tmp = []
    for elem in var_list:
      if isinstance(elem.ptr.type, c_ast.ArrayDecl):
         name = elem.name + "_cu"
      else:
         if pointer:
             name = " &" + elem.name
         elif value:
             name = " * " + elem.name
         else:
             name = " " + elem.name
      tmp += [name]
    return ', '.join(tmp)

def rename_if_complex(var, prefix = '', suffix = '',):
    if is_complex_type(var):
        return prefix + var.name + suffix
    else:
        return var.name

########################################################
#               New-Style backend                      #
########################################################

# TODO: Move this to a proper place
def getTypedef(elem):
    """ Returns the symbol table of a given node.
         It search the parent FileAST and returns its ST
         If no FileAST, raises NodeNotValid
    """
    from yacf.Frontend.SymbolTable import getSymbolTableFromNode
    st = getSymbolTableFromNode(elem)
    node = st.lookUpTypeDecl(elem)
    return node


def is_array(elem):
    """ Check if elem is an array decl 

    """
    if isinstance(elem.type, c_ast.TypeDecl):
        elem = getTypedef(elem.type)
    if isinstance(elem.type, c_ast.ArrayDecl):
        return True
    return False


def is_struct(elem):
    """ Check if elem is an array decl 

    """
    if isinstance(elem.type, c_ast.TypeDecl):
        elem = getTypedef(elem.type)
    if isinstance(elem.type, c_ast.Struct):
        return True
    return False

def is_pointer(elem):
    """ Check if elem is a Pointer

    """
    if isinstance(elem.type, c_ast.PtrDecl):
        return True
    return False

def is_complex_type(elem):
#    return is_array(elem) or is_struct(elem) or is_pointer(elem)
    return is_array(elem) or is_pointer(elem)






def write_left_part_of_var_decl(elem):
        """ Write the declaration of the type of an element
                in the original language.
        
            Minor modifications are needed.
        
            :param elem: Declaration node
        """
        tmp = ""
        if hasattr(elem, 'storage'):
            tmp = " ".join(['%s'%stor for stor in elem.storage]) 
        if isinstance(elem.type, c_ast.ArrayDecl):
#            import pdb
#            pdb.set_trace()
            tmp += " "
            tmp_node = elem.type
            while not (isinstance(tmp_node, c_ast.TypeDecl) or isinstance(tmp_node, c_ast.PtrDecl)) and tmp_node:
                tmp_node = tmp_node.type

            tmp += str(tmp_node) #self.fast_write(tmp_node)
        else:
            tmp += " ".join(['%s'%qual for qual in elem.quals])
            tmp += " "
            tmp += str(elem.type) # self.fast_write(elem.type)
        return tmp.replace(';','').replace('\n','')

def varlist_to_funccall(var_list,):
    """ Write var_list as a parameter list for a function call
    """
    tmp = []
    for elem in var_list:
        tmp += [elem.name]
    return ', '.join(tmp)

def var_to_declaration(decl_node):
    """ Return the left part of a var declaration """
    if type(decl_node) == Symbol:
        decl_node = decl_node.node
    init = decl_node.init
    decl_node.init = None
    decl_str =  str(decl_node) # write_left_part_of_var_decl(decl_node) + " " + decl_node.name
    decl_node.init = init
    return decl_str

def id_to_var_node(id_node):
    """
        :return: Decl node of id
    """
    return decl_of_id(id_node, getFileNode(id_node))

def varlist_to_funcproto(var_list,):
    """ Write var_list as a parameter list for function parameters
    """
    if not var_list:
        return ""
    
    tmp = []
        
    for elem in var_list:
        tmp += [var_to_declaration(elem)]
    return ', '.join(tmp)

def size_of_var(var,):
    # If we have previously computed the sizeExpression, use it here
    if hasattr(var, 'sizeExpression_subtree'):
        return var.sizeExpression_subtree
    sizeof_ast = c_ast.UnaryOp(op = 'sizeof', expr = None)
    try:
        if hasattr(var, 'numElems'):
            if not isinstance(var.type, c_ast.PtrDecl):
                sizeof_ast.expr = var.type
                return c_ast.BinaryOp(op = '*', left = var.numElems, right = sizeof_ast) 
            else:
                # Case its a pointer, the size is not the pointer itself but the size 
                #  of the object that is referencing
                sizeof_ast.expr = var.type.type
                return c_ast.BinaryOp(op = '*', left = var.numElems, right = sizeof_ast) 
        else:
            if not isinstance(var.type, c_ast.PtrDecl):
                sizeof_ast.expr = var.type
                return sizeof_ast
                # return partial_parse("sizeof(" + str(var.type) + ")")
            else:
                # Case its a pointer, the size is not the pointer itself but the size 
                #  of the object that is referencing
                # return partial_parse("sizeof(" + str(var.type.type) + ")")
                sizeof_ast.expr = var.type.type
                return sizeof_ast
    except:
        print " Error while trying to compute the size of a variable. Maybe the numElems attribute was missing but needed?"
        # TODO: Implement an exception for this
        raise NotImplemented



def array_to_ptr(varlist):
    """ If a declaration within the list is an array, it is replaced by a ptr declaration """
    tmp = []
    for elem in varlist:
#	 	if isinstance(elem.type, c_ast.ArrayDecl):
#			elem2 = elem.__deepcopy__();
#			setattr(elem2, 'type', c_ast.PtrDecl(type=elem2.type.type, quals = []))
#			# TODO: Several nested arrays will need to repeat this process
#			# Add the new element to the list
#			tmp.append(elem2)
#		else:
			tmp.append(elem)
    return tmp




def write_gmacKernelArgs(parameters):
    tmp = [];
    for var in parameters:
        if not var.frangolloType == "VAR_SCALAR":
            tmp.append("gmacPtr( " + var.name + ")")
        else:
            tmp.append(var.name)
    return tmp

